# -*- coding: utf-8 -*-
"""
Created on 2021-6-6 09:27:34
客户端主体
@author: 王韬
gitee: https://gitee.com/w3t2/cardgame.git
"""

import pygame
from pygame.locals import *
import socket
import sys
import threading
import time

import classes as cls
import functions as fn

# ###################<<定义常量>>################### #
HALL_WIDTH = 1024  # 大厅窗口大小
HALL_HEIGHT = 540

ROOM_WIDTH = 1024  # 房间窗口大小
ROOM_HEIGHT = 768

CARD_WIDTH = 150  # 卡牌大小
CARD_HEIGHT = 200

RECV_EVENT = USEREVENT
# ###################<<初始化变量>>################### #
# ###################<<服务器变量>>################### #
rlock = threading.RLock()
sk = None    # 服务器socket
addr = None  # 服务器地址
hall_args = {
    'is_connected': False,  # 是否连接
    'p_num': 0,             # 当前在线人数
    'is_waiting': False,    # 是否在匹配中
    'in_game': False        # 是否在游戏中
}
room_args = {
    'r_index': -1,    # 房间号
    'turn_num': 0,    # 回合数
}
game_args = {
    'is_def': True,  # 守方
    'turn_num': 0,  # 回合数
    'my_cards': [],  # 守方手牌列表
    'his_cards': [],  # 攻方手牌列表
    'wait_flag': False,
    'obj': {
        'hp': 100
    },
    'me': {
        'wait_oprt': False,
        'turn_flag': False,
        'win_flag': False,
        'init_card_num': 0,
        'card_num': 0,
        'card_choose': 0,

    },
    'him': {
        'wait_oprt': False,
        'turn_flag': False,
        'win_flag': False,
        'init_card_num': 0,
        'card_num': 0,
        'card_choose': 0,

    },
}

CARDS = None


# ###################<<功能函数>>################### #
# ###################<<接收消息函数>>################### #
def recv_msg():
    """连接服务端, 接收消息

    Arguments:
    Returns:
    """
    global sk
    global addr
    sk = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    addr = ('10.122.244.191', 10000)
    # addr = (server_ip, 10000)

    try:
        sk.connect(addr)
    except ConnectionRefusedError:
        print('cannot connect to server')
    else:
        hall_args['is_connected'] = True
        print('connected to server')

    while_con = True
    while while_con:
        try:
            recv_data = sk.recv(1024)
            recv_data = recv_data.decode("utf-8")
            # print(type(recv_data))
        except (ConnectionResetError, OSError):
            print("服务器关闭, 连接断开")
            sk.close()
            while_con = False
        else:
            if recv_data:
                try:
                    recv_args = eval(recv_data)
                except:
                    # print("Unexpected error:", sys.exc_info()[0])
                    print(recv_data, 'type:', type(recv_data))
                else:
                    for key in recv_args.keys():
                        # global hall_args
                        # global room_args
                        if key in hall_args.keys():
                            rlock.acquire()
                            hall_args[key] = recv_args[key]
                            rlock.release()
                            print('receive hall_args '+key+': ', hall_args[key])
                            if key == 'is_connected':
                                if not recv_args[key]:
                                    while_con = False
                        elif key in room_args.keys():
                            rlock.acquire()
                            room_args[key] = recv_args[key]
                            rlock.release()
                            print('receive room_args '+key+': ', room_args[key])
                        elif key in game_args.keys():
                            rlock.acquire()
                            game_args[key] = recv_args[key]
                            # print('receive game_args ' + k + ': ', game_args[k])
                            print('receive game_args ' + key + ': ', game_args[key])
                            rlock.release()
                            if key == 'my_cards':
                                try:
                                    event = pygame.event.Event(USEREVENT+1)
                                    pygame.event.post(event)
                                except pygame.error as e:
                                    print(e)

                    try:
                        event = pygame.event.Event(RECV_EVENT, dict(msg='recv'))
                        pygame.event.post(event)
                    except pygame.error:
                        print(pygame.error)
                    # print(recv_args)
                    # print(type(recv_args))


def send_msg(data):
    """发送消息

    Arguments:
        data -- data about to send to server
    Returns:
    """
    # global sk
    try:
        send_data = str(data).encode('utf-8')
        sk.send(send_data)
    except:
        print('sending fail:', data)


# ###################<<用户窗口函数>>################### #
def user_window():
    """user window"""
    print("start user window")

    # main loop
    while True:
        # 如果在大厅中，启动大厅窗口
        if not hall_args['in_game']:
            hall_window()
        # 如果在游戏中，启动房间窗口
        elif hall_args['in_game']:
            room_window()


def hall_window():
    """hall window"""

    # initial
    pygame.init()
    screen = pygame.display.set_mode((HALL_WIDTH, HALL_HEIGHT))
    pygame.display.set_caption('card game hall')

    # fill background
    bg_image, bg_image_rect = fn.load_img('hall_bg.png')
    bg = pygame.Surface(screen.get_size()).convert()
    bg.blit(bg_image, (0, 0))
    # bg.fill((190, 145, 23))

    # show some text
    font = pygame.font.Font(None, 36)
    font_h1 = pygame.font.Font(None, 100)
    title = font_h1.render('Save that young guy!', True, (255, 0, 0))
    player_num = font.render('players online: '+str(hall_args['p_num']),
                             True, (40, 123, 222))

    # show buttons
    hall_btns = []

    btn_find_match = cls.Button(
        'find match',
        (0, HALL_HEIGHT - 40),
        (200, 40))
    hall_btns.append(btn_find_match)
    btn_cancel_find = cls.Button(
        'cancel',
        (200, HALL_HEIGHT - 40),
        (100, 40))
    btn_cancel_find.txt_color = (60, 63, 65)
    btn_cancel_find.fill_color = (43, 43, 43)
    btn_cancel_find.update_btn()
    hall_btns.append(btn_cancel_find)

    # blit
    # bg.blit(title, (HALL_WIDTH//2 - title.get_width()//2, 150))
    bg.blit(title, (0, 0))
    bg.blit(player_num, (0, 100))
    bg.blit(btn_find_match.surface, btn_find_match.pos)
    bg.blit(btn_cancel_find.surface, btn_cancel_find.pos)
    screen.blit(bg, (0, 0))
    pygame.display.update()

    # hall loop
    while True:
        if not hall_args['is_connected']:
            hall_args['is_waiting'] = False
            hall_args['in_game'] = False
        elif hall_args['in_game']:
            pygame.display.quit()
            room_window()
            send_data = dict(is_connected=False)
            send_msg(send_data)
            sys.exit()

        for event in pygame.event.get():
            if event.type == QUIT:
                send_data = dict(is_connected=False)
                send_msg(send_data)
                sys.exit()
            elif event.type == MOUSEBUTTONDOWN:
                for btn_num in range(len(hall_btns)):
                    btn_check, btn_name = hall_btns[btn_num].check_click(pygame.mouse.get_pos())
                    if btn_check:
                        hall_event(btn_name, bg, hall_btns)
                    # bg.blit(hall_btns[btn_num].surface, hall_btns[btn_num].pos)

        player_num = font.render('players online: ' + str(hall_args['p_num']),
                                 True, (40, 123, 222))
        bg.blit(player_num, (0, 100))

        screen.blit(bg, (0, 0))
        pygame.display.update()


def hall_event(event, bg, hall_btns):
    """大厅事件响应

    Arguments:
        event -- name of event, str
    Keyword arguments:

    """
    if event == 'find match':
        hall_find_match(bg, hall_btns)
    elif event == 'cancel':
        hall_cancel_find(bg, hall_btns)


def hall_find_match(bg, hall_btns):
    """匹配按钮响应函数

    """
    # 大厅显示响应
    hall_btns[0].fill_color = (43, 43, 43)
    hall_btns[0].txt_color = (60, 63, 65)
    hall_btns[0].update_btn()
    bg.blit(hall_btns[0].surface, hall_btns[0].pos)

    hall_btns[1].fill_color = (61, 61, 62)
    hall_btns[1].txt_color = (66, 156, 227)
    hall_btns[1].update_btn()
    bg.blit(hall_btns[1].surface, hall_btns[1].pos)

    font = pygame.font.Font(None, 36)
    text = font.render('waiting...', True, (73, 156, 84))
    text_bg = pygame.Surface((200, 30))
    text_bg.fill((0, 0, 0))
    text_bg.blit(text, (0, 0))
    bg.blit(text_bg, (0, HALL_HEIGHT - 70))

    # 服务器参数响应
    hall_args['is_waiting'] = True
    send_data = dict(is_waiting=True)
    send_msg(send_data)


def hall_cancel_find(bg, hall_btns):
    """取消匹配按钮响应函数

    """
    # 大厅显示响应
    hall_btns[0].fill_color = (61, 61, 62)
    hall_btns[0].txt_color = (66, 156, 227)
    hall_btns[0].update_btn()
    bg.blit(hall_btns[0].surface, hall_btns[0].pos)

    hall_btns[1].fill_color = (43, 43, 43)
    hall_btns[1].txt_color = (60, 63, 65)
    hall_btns[1].update_btn()
    bg.blit(hall_btns[1].surface, hall_btns[1].pos)

    font = pygame.font.Font(None, 36)
    text = font.render('', True, (73, 156, 84))
    text_bg = pygame.Surface((200, 30))
    text_bg.fill((0, 0, 0))
    text_bg.blit(text, (0, 0))
    bg.blit(text_bg, (0, HALL_HEIGHT - 70))

    # 服务器参数响应
    hall_args['is_waiting'] = False
    send_data = dict(is_waiting=False)
    send_msg(send_data)


def hall_match_found(bg, hall_btns):
    """已找到对局响应"""
    # 大厅显示响应
    hall_btns[0].fill_color = (43, 43, 43)
    hall_btns[0].txt_color = (60, 63, 65)
    hall_btns[0].update_btn()
    bg.blit(hall_btns[0].surface, hall_btns[0].pos)

    hall_btns[1].fill_color = (43, 43, 43)
    hall_btns[1].txt_color = (60, 63, 65)
    hall_btns[1].update_btn()
    bg.blit(hall_btns[1].surface, hall_btns[1].pos)

    font = pygame.font.Font(None, 36)
    text = font.render('match found!', True, (73, 156, 84))
    text_bg = pygame.Surface((200, 30))
    text_bg.fill((0, 0, 0))
    text_bg.blit(text, (0, 0))
    bg.blit(text_bg, (0, HALL_HEIGHT - 70))


def room_window():
    """room window"""

    # initial
    # pygame.init()
    screen = pygame.display.set_mode((ROOM_WIDTH, ROOM_HEIGHT))
    pygame.display.set_caption('card game room')
    global CARDS
    CARDS = cls.CardDict()
    clock = pygame.time.Clock()

    # fill background
    bg_image, bg_image_rect = fn.load_img('room_bg.png')
    bg = pygame.Surface(screen.get_size()).convert()
    bg.blit(bg_image, (0, 0))

    # show some text
    font = pygame.font.Font(None, 100)
    font1 = pygame.font.Font(None, 36)
    # txt_hp = font.render('HP:', True, (64, 106, 88))
    txt_hp = cls.Txt('HP :100', (300, 150), font, (43, 43, 43))
    txt_turn = cls.Txt('turn_flag', (150, 50), font1, (199, 84, 80))

    # show some buttons
    btns = []

    btn_end_turn = cls.Button(
        'end turn',
        (200, 500),
        (200, 40)
    )
    btns.append(btn_end_turn)

    btn_confirm = cls.Button(
        'confirm',
        (480, 500),
        (200, 40)
    )
    btns.append(btn_confirm)

    # blit
    for btn in btns:
        bg.blit(btn.surface, btn.pos)

    screen.blit(bg, (0, 0))
    pygame.display.update()

    # delay to make sure sever works
    time.sleep(2)

    # set p_flag
    if game_args['is_def']:
        p_flag = 'me'
    else:
        p_flag = 'him'

    # groups
    card_grp = pygame.sprite.Group()
    card_slc = pygame.sprite.Group()
    card_confirm = pygame.sprite.Group()
    txt_grp = pygame.sprite.Group()

    # room loop
    while True:
        # clock.tick(60)
        if not hall_args['is_connected']:
            if 0 < game_args['obj']['hp'] < 200:
                print("对方断开连接", '对局已终止')
                send_data = dict(is_connected=False)
                send_msg(send_data)

                # 断连提示 确认后退出
                return
            else:
                # 胜负提示
                font = pygame.font.Font(None, 200)
                if game_args[p_flag]['win_flag']:
                    u_msg = font.render('You win!!', True, (255, 238, 45))
                else:
                    u_msg = font.render('You lose!!', True, (175, 177, 179))
                bg.blit(u_msg, (ROOM_WIDTH//2 - u_msg.get_width()//2, ROOM_HEIGHT//2 - u_msg.get_height()))
                screen.blit(bg, (0, 0))
                pygame.display.update()

        for event in pygame.event.get():
            if event.type == QUIT:
                pygame.display.quit()
                room_window_end()
                print('room window quited')
                return
            elif event.type == MOUSEBUTTONDOWN:
                mouse_pos = pygame.mouse.get_pos()
                for btn_num in range(len(btns)):
                    btn_check, btn_name = btns[btn_num].check_click(mouse_pos)
                    if btn_check and game_args[p_flag]['turn_flag']:
                        # room_event(btn_name, bg, btns)
                        if btn_name == 'confirm' and len(card_slc) == 1:
                            card_confirm.empty()
                            card_obj = card_slc.sprites()[0]
                            card_obj.rect.left = 0
                            card_obj.rect.top = 0
                            card_confirm.add(card_obj)
                            card_slc.remove(card_obj)

                            game_args[p_flag]['wait_oprt'] = False
                            game_args[p_flag]['card_choose'] = card_obj.index
                            if p_flag == 'me':
                                send_data = dict(me=game_args[p_flag])
                            else:
                                send_data = dict(him=game_args[p_flag])
                            send_msg(send_data)

                        elif btn_name == 'end turn':
                            game_args[p_flag]['turn_flag'] = False
                            if p_flag == 'me':
                                send_data = dict(me=game_args[p_flag])
                            else:
                                send_data = dict(him=game_args[p_flag])
                            send_msg(send_data)

                for card in card_grp.sprites():
                    if card.rect.collidepoint(mouse_pos) and not card.selected:
                        card.rect.move_ip(0, -30)
                        card.selected = True
                        card_slc.add(card)
                    elif card.selected:
                        card.rect.move_ip(0, 30)
                        card.selected = False
                        card_slc.remove(card)

            elif event.type == USEREVENT+1:
                # 收到手牌变化
                card_grp.clear(bg, bg_image)
                card_grp.empty()
                set_card_grp(p_flag, card_grp)

                # 更新hp
                txt_grp.clear(bg, bg_image)
                txt_grp.empty()

                hp = game_args['obj']['hp']
                if hp >= 100:
                    clr = (73, 156, 84)
                else:
                    clr = (234, 79, 31)
                pos = (ROOM_WIDTH//2, 0)

                txt_hp.set(clr, 'HP: '+str(hp), (43, 43, 43), pos)
                if game_args[p_flag]['turn_flag']:
                    txt_turn.set(clr, 'Your Turn!!', (43, 43, 43), (ROOM_WIDTH-150, 0))
                else:
                    txt_turn.set(clr, 'Enemy Turn!!', (43, 43, 43), (ROOM_WIDTH-150, 0))

                txt_grp.add(txt_hp)
                txt_grp.add(txt_turn)

        # blit
        card_grp.clear(bg, bg_image)
        card_confirm.clear(bg, bg_image)
        txt_grp.clear(bg, bg_image)

        card_grp.draw(bg)
        card_confirm.draw(bg)
        txt_grp.draw(bg)
        screen.blit(bg, (0, 0))
        pygame.display.update()


def room_event(name, bg, btns):
    """room event response

    Arguments:
        name -- event name
    """
    pass


def set_card_grp(p_flag, card_grp):
    if p_flag == 'me':
        card_flag = 'my_cards'
    else:
        card_flag = 'his_cards'

    card_num = len(game_args[card_flag])
    for k in range(card_num):
        card_index = game_args[card_flag][k]
        new_card = create_card(p_flag, card_index, k)

        left = k * CARD_WIDTH
        top = ROOM_HEIGHT - CARD_HEIGHT
        new_card.rect.move_ip(left, top)

        card_grp.add(new_card)


def create_card(p_flag, card_index, index):
    """create a card by index"""

    if p_flag == 'me':
        if card_index == 0:
            card_name = 'regenerate'
            card_attr = 30
        elif card_index == 1:
            card_name = 'regenerate'
            card_attr = 20
        elif card_index == 2:
            card_name = 'regenerate'
            card_attr = 5
        elif card_index == 3:
            card_name = 'regenerate'
            card_attr = 10
    else:
        if card_index == 0:
            card_name = 'attack'
            card_attr = 50
        elif card_index == 1:
            card_name = 'attack'
            card_attr = 10
        elif card_index == 2:
            card_name = 'attack'
            card_attr = 20
        elif card_index == 3:
            card_name = 'attack'
            card_attr = 30

    new_card = CARDS.get_card(card_name, card_attr)
    new_card.index = index
    return new_card


def room_window_end():
    """room window end

    Arguments:
    """
    if hall_args['is_connected']:
        send_data = dict(end_flag=True)
        send_msg(send_data)


# ###################<<主函数入口>>################### #
def client_main():
    """客户端主函数"""
    t_list = []
    # 通信进程
    t_recv = cls.MyThread('t_recv', recv_msg)
    t_list.append(t_recv)
    # 窗口进程
    t_user = cls.MyThread('t_user', user_window)
    t_list.append(t_user)

    for t in t_list:
        t.start()

    for t in t_list:
        t.join()


if __name__ == "__main__":
    client_main()

